package com.tool.app.utils;

import org.bytedeco.javacpp.Loader;
import org.bytedeco.opencv.opencv_java;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgproc.Moments;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public class StampDetector {

	//============= 关键调节参数 =============//
	/* 【颜色阈值】调节红章检测敏感度 */
	private static final int HSV_SAT_MIN = 40;    // 最小饱和度(30-70)（值越小能检测越浅的红色）
	private static final int HSV_VAL_MIN = 40;    // 最小明度(30-70)（值越小能检测越暗的印章）

	/* 【形状过滤】调节圆形检测严格度 */
	private static final int MIN_AREA = 3000;      // 最小轮廓面积（过滤噪点）
	private static final int MIN_CIRCLE_EDGES = 8; // 最小边数（值越大圆形检测越严格）
	private static final double MIN_AREA_RATIO = 0.8; // 轮廓面积/外接圆面积最小比值
	private static final int MIN_RADIUS = 50;   //印章最小半径
	private static final int MAX_RADIUS = 500;  //印章最大半径

	/* 【中心图案】调节五角星检测 */
	private static final double CENTER_ROI_RATIO = 0.3; // 中心检测区域比例（相对于半径）
	private static final int STAR_MIN_POINTS = 9;  //最小凹凸点
	private static final int MIN_CENTER_AREA = 500;      // 最小中心面积
	private static final AtomicBoolean loaded = new AtomicBoolean(false);

	public static void load() {
		if (loaded.compareAndSet(false, true)) { // CAS操作
			Loader.load(opencv_java.class);
			System.out.println("OpenCV 版本: " + Core.VERSION);
		}
	}

	public static void main(String[] args) throws IOException {
		System.out.println("开始检测...");
		long t1 = System.currentTimeMillis();
		String filePath = "C:\\Users\\t480\\Pictures\\zhang4.png";
		boolean hasStar = isTrueStamp(filePath);
		System.out.println("检测结果: " + (hasStar ? "有效印章" : "无效印章"));
		long t2 = System.currentTimeMillis();
		System.out.println("t2-t1 = " + (t2 - t1));
	}

	public static boolean isTrueStamp(String filePath) throws IOException {
		Mat src;
		if (filePath.endsWith(".pdf")) {
			Path path = Paths.get(filePath);
			byte[] bytes = Files.readAllBytes(path);
			BufferedImage image = PdfUtil.getLastPdfPageImage(bytes);
			src = bufferedImageToMat(image);
		} else {
			src = Imgcodecs.imread(filePath);
		}
		debugWrite("input.jpg", src);
		return isTrueStamp(src);
	}


	public static Mat bufferedImageToMat(BufferedImage image) {
		// 确保图像是兼容格式（3字节BGR或灰度）
		if (image.getType() != BufferedImage.TYPE_3BYTE_BGR &&
				image.getType() != BufferedImage.TYPE_BYTE_GRAY) {
			// 转换为标准格式
			BufferedImage converted = new BufferedImage(
					image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
			converted.getGraphics().drawImage(image, 0, 0, null);
			image = converted;
		}
		// 将 BufferedImage 转换为字节数组
		byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
		// 创建 Mat 对象
		Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
		// 将字节数组数据放入 Mat 中
		mat.put(0, 0, data);
		return mat;
	}

	public static boolean isTrueStamp(Mat src) {
		// 检查图像是否有效
		if (src.empty()) return false;

		// 2. 红色区域检测
		Mat redMask = detectRedArea(src);

		// 3. 查找轮廓
		List<MatOfPoint> contours = new ArrayList<>();
		Mat hierarchy = new Mat();
		/**
		 * findContours参数解析：
		 * redMask.clone()：复制掩膜（避免修改原数据）
		 * RETR_EXTERNAL：只检测最外层轮廓
		 * CHAIN_APPROX_SIMPLE：压缩轮廓点（减少内存占用）
		 */
		Imgproc.findContours(redMask.clone(), contours, hierarchy,
				Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

		// 4. 精确形状分析
		for (MatOfPoint contour : contours) {
			// 计算轮廓面积
			double area = Imgproc.contourArea(contour);
			if (area < MIN_AREA) continue;   // 过滤小面积噪点
			// 4.1 将整数坐标点转为浮点型，供后续精确计算使用 CV_32FC2：32位浮点+2通道（存储x,y坐标）
			MatOfPoint2f contour2f = new MatOfPoint2f();
			contour.convertTo(contour2f, CvType.CV_32FC2);

			// 4.2 多边形逼近
			MatOfPoint2f approxCurve = new MatOfPoint2f();
			//计算轮廓周长  0.015为允许的误差范围
			double epsilon = 0.015 * Imgproc.arcLength(contour2f, true);
			//用线条套取轮廓,    epsilon: 逼近精度, 值越小, 多边形边数越多, true表示闭合轮廓
			Imgproc.approxPolyDP(contour2f, approxCurve, epsilon, true);


			// 4.3 最小外接圆（使用MatOfPoint2f）
			Point center = new Point();
			float[] radius = new float[1];
			Imgproc.minEnclosingCircle(approxCurve, center, radius); // center圆心坐标 , radius圆半径（通过数组返回）

			// 4.4 计算关键指标
			double circleArea = Math.PI * radius[0] * radius[0];
			double areaRatio = area / circleArea;  // 轮廓面积/外接圆面积 面积比：真圆≈1.0，矩形≈0.785
			int edgeCount = approxCurve.rows(); // 多边形边数

			// 调试信息
			System.out.printf("轮廓分析: 边数=%d 面积=%.2f 面积比=%.2f 半径=%.1f 位置=(%.0f,%.0f)%n",
					edgeCount, area, areaRatio, radius[0], center.x, center.y);

			// 4.5 综合判断（严格条件）
			if (isValidCircle(edgeCount, areaRatio, radius[0])) {
				return hasStarInCenter(redMask, center, radius[0]);
			}
		}
		return false;
	}

	private static boolean isValidCircle(int edgeCount, double areaRatio, float radius) {
		return edgeCount >= MIN_CIRCLE_EDGES &&           // 足够多的边数（接近圆）
				areaRatio > MIN_AREA_RATIO &&        // 面积接近外接圆
				areaRatio < 1.1 &&       // 排除不规则形状
				radius > MIN_RADIUS &&           // 最小半径限制
				radius < MAX_RADIUS;            // 最大半径限制
	}

	/**
	 * 作用：生成只包含红色区域的二值化掩膜（白色=红色区域）
	 *
	 * @param src
	 * @return
	 */
	private static Mat detectRedArea(Mat src) {
		Mat hsv = new Mat();
		//转HSV空间 HSV比RGB空间更容易分离颜色
		Imgproc.cvtColor(src, hsv, Imgproc.COLOR_BGR2HSV);

		// 2. 动态阈值参数（根据图像亮度自适应）
		Scalar meanVal = Core.mean(hsv);
		int satMin = (int) Math.max(30, Math.min(50, meanVal.val[1]));
		// 红色阈值（HSV空间双范围）
		Mat redMask1 = new Mat(), redMask2 = new Mat();
		// 检测色环左端的红色（0-10°）（橙红 → 正红）  如果印章为浅红色调低第一个Scalar中间参数
		Core.inRange(hsv, new Scalar(0, satMin, 30), new Scalar(10, 255, 255), redMask1);
		// 检测色环右端的红色（160-180°）（紫红 → 正红）
		Core.inRange(hsv, new Scalar(160, satMin, 30), new Scalar(180, 255, 255), redMask2);

		// 合并结果 红色印章区域变成白色（255）
		Mat redMask = new Mat();
		Core.bitwise_or(redMask1, redMask2, redMask);

		// 形态学处理
		// 创建一个椭圆形的"核"
		Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, getAdaptiveKernelSimple(src));
		// 闭运算：先膨胀(填充印章内部 "核" 大小孔洞 ) 后腐蚀(去噪点)
		Imgproc.morphologyEx(redMask, redMask, Imgproc.MORPH_CLOSE, kernel);


		//输出中间图像观察红色掩膜
		debugWrite("red_mask.jpg", redMask);
		return redMask;
	}

	private static Size getAdaptiveKernelSimple(Mat image) {
		// 基于图像面积的开平方（经验公式）
		int base = (int) (Math.sqrt(image.width() * image.height()) * 0.005);

		// 限制范围并确保奇数
		base = Math.max(3, Math.min(21, base));

		return new Size(base, base * 0.9);
	}

	/**
	 * 快速检测五角星（仅需满足：10个顶点 + 中心位置）
	 *
	 * @param redMask     红色掩膜（白色=印章区域）
	 * @param stampCenter 印章圆心
	 * @param stampRadius 印章半径
	 */
	private static boolean hasStarInCenter(Mat redMask, Point stampCenter, float stampRadius) {
		// 1. 截取圆心区域（半径的1范围）
		int roiSize = (int) (stampRadius * CENTER_ROI_RATIO * 2);
		int x = Math.max(0, (int) (stampCenter.x - (double) roiSize / 2));
		int y = Math.max(0, (int) (stampCenter.y - (double) roiSize / 2));
		int w = Math.min(redMask.cols() - x, roiSize);
		int h = Math.min(redMask.rows() - y, roiSize);
		Mat roi = new Mat(redMask, new Rect(x, y, w, h));
		//调试 可选
		debugWrite("debug_star_" + stampCenter.x + "_" + stampCenter.y + ".jpg", roi);

		// 优化方案（减少噪声干扰）
		// 2. 边缘增强（您指定的切割后Canny）
		Mat edges = new Mat();
		Imgproc.GaussianBlur(roi, roi, new Size(3, 3), 0); // 轻度降噪
		Imgproc.Canny(roi, edges, 30, 90); // 优化阈值
		Core.bitwise_or(roi, edges, roi);

		// 2. 查找轮廓
		List<MatOfPoint> contours = new ArrayList<>();
		Imgproc.findContours(roi.clone(), contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

		for (MatOfPoint contour : contours) {
			// 计算轮廓面积
			double area = Imgproc.contourArea(contour);
			if (area < MIN_CENTER_AREA) continue;   // 过滤小面积噪点
			System.out.println("area = " + area);


			MatOfPoint2f approx = new MatOfPoint2f();
			// 多边形逼近（精度=周长的2%）
			Imgproc.approxPolyDP(
					new MatOfPoint2f(contour.toArray()),
					approx,
					0.02 * Imgproc.arcLength(new MatOfPoint2f(contour.toArray()), true),
					true
			);

			// 4. 关键判断：顶点数=10 + 中心偏移<半径的1/5  五角星由 5个外凸顶点（尖角）和 5个内凹顶点（凹陷处）交替组成，共10个顶点
			System.out.println("approx.rows() = " + approx.rows());
			if (approx.rows() >= STAR_MIN_POINTS) {

				Moments m = Imgproc.moments(contour);
				Point contourCenter = new Point(
						x + m.m10 / m.m00, // 转换为全局坐标
						y + m.m01 / m.m00
				);
				double dist = Math.sqrt(Math.pow(contourCenter.x - stampCenter.x, 2) +
						Math.pow(contourCenter.y - stampCenter.y, 2));
				if (dist < stampRadius * CENTER_ROI_RATIO) {
					return true;
				}
				//凸性缺陷检测 不太准
//				MatOfInt hull = new MatOfInt();
//				Imgproc.convexHull(contour, hull);
//				MatOfInt4 defects = new MatOfInt4();
//				Imgproc.convexityDefects(contour, hull, defects);
//
//				// 统计有效缺陷（深度>阈值）
//				long validDefects = Arrays.stream(defects.toArray())
//						.filter(d -> d > 20)
//						.count();
//				return validDefects >= 5; // 五角星应有5个明显凹陷


			}
		}
		return false;
	}

	//==================== 调试====================//
	private static void debugWrite(String path, Mat mat) {
		Imgcodecs.imwrite(path, mat);
	}
}